#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Docker备份工具启动器
提供友好的菜单界面来管理备份流程
"""

import os
import sys
import json
import subprocess
import shutil
import glob
from pathlib import Path

def install_and_import(package):
    try:
        __import__(package)
    except ModuleNotFoundError:
        choice = input(f"缺少依赖包 '{package}'，是否自动安装？(y/n): ")
        if choice.lower() == 'y':
            subprocess.check_call([sys.executable, "-m", "pip", "install", package])
            print(f"已安装 {package}，请重新运行程序。")
            sys.exit(0)
        else:
            print(f"请手动安装依赖包：{package}")
            sys.exit(1)

install_and_import('paramiko')
install_and_import('pexpect')
install_and_import('tqdm')

from docker_backup_cli import ConfigManager, ServerDockerBackupTool
import getpass

class BackupLauncher:
    def __init__(self):
        self.config_manager = ConfigManager(config_file="config/config.json")
        self.config = self.config_manager.config
        
    def show_menu(self):
        """显示主菜单"""
        print("\n" + "=" * 60)
        print("🐳 绿谷云服务器docker化迁移工具")
        print("=" * 60)
        print("1. 远程备份")
        print("2. 本地备份")
        print("3. 清除临时文件")
        print("0. 退出")
        print("=" * 60)
        
    def find_certificates(self):
        """查找可用的证书文件"""
        cert_extensions = ['.pem', '.key', '.crt', '.p12']
        cert_files = []
        
        # 查找当前目录下的证书文件
        for ext in cert_extensions:
            cert_files.extend(glob.glob(f"*{ext}"))
            
        # 查找常见的证书目录
        cert_dirs = ['~/.ssh', '~/.cert', './certs', './keys', './skey', 'skey']
        for cert_dir in cert_dirs:
            expanded_dir = os.path.expanduser(cert_dir)
            if os.path.exists(expanded_dir):
                for ext in cert_extensions:
                    cert_files.extend(glob.glob(f"{expanded_dir}/*{ext}"))
        
        # 使用绝对路径去重，避免显示重复文件
        unique_certs = []
        seen_paths = set()
        
        for cert_file in cert_files:
            abs_path = os.path.abspath(cert_file)
            if abs_path not in seen_paths:
                seen_paths.add(abs_path)
                unique_certs.append(cert_file)
        
        return unique_certs
        
    def select_certificate(self):
        """选择证书文件"""
        print("\n🔐 证书登录设置")
        cert_files = self.find_certificates()
        
        if not cert_files:
            print("❌ 未找到可用的证书文件")
            cert_path = input("请手动输入证书文件路径: ").strip()
            if not os.path.exists(cert_path):
                print("❌ 证书文件不存在")
                return None
            return self.fix_certificate_permissions(cert_path)
            
        print("📋 找到以下证书文件:")
        for i, cert_file in enumerate(cert_files, 1):
            print(f"  {i}. {cert_file}")
        print(f"  {len(cert_files) + 1}. 手动输入路径")
        
        while True:
            try:
                choice = input(f"\n请选择证书文件 (1-{len(cert_files) + 1}): ").strip()
                choice_num = int(choice)
                
                if 1 <= choice_num <= len(cert_files):
                    selected_cert = cert_files[choice_num - 1]
                    print(f"✅ 已选择证书: {selected_cert}")
                    return self.fix_certificate_permissions(selected_cert)
                elif choice_num == len(cert_files) + 1:
                    cert_path = input("请手动输入证书文件路径: ").strip()
                    if os.path.exists(cert_path):
                        print(f"✅ 已选择证书: {cert_path}")
                        return self.fix_certificate_permissions(cert_path)
                    else:
                        print("❌ 证书文件不存在")
                else:
                    print("❌ 无效选择，请重新输入")
            except ValueError:
                print("❌ 请输入有效数字")
                
    def fix_certificate_permissions(self, cert_path):
        """修正证书文件权限"""
        try:
            # 获取当前权限
            current_mode = os.stat(cert_path).st_mode & 0o777
            
            # 检查权限是否为600（仅所有者可读写）
            if current_mode != 0o600:
                print(f"⚠️  证书文件权限不正确: {oct(current_mode)}")
                print("🔧 正在修正证书文件权限...")
                
                # 修改权限为600
                os.chmod(cert_path, 0o600)
                
                # 验证权限修改
                new_mode = os.stat(cert_path).st_mode & 0o777
                if new_mode == 0o600:
                    print("✅ 证书文件权限已修正为600")
                else:
                    print(f"❌ 权限修正失败，当前权限: {oct(new_mode)}")
                    return None
            else:
                print("✅ 证书文件权限正确")
                
            return cert_path
            
        except Exception as e:
            print(f"❌ 修正证书文件权限失败: {e}")
            return None
        
    def check_sshpass(self):
        """检查sshpass是否可用"""
        if shutil.which("sshpass") is None:
            print("⚠️  未找到sshpass程序，需要安装")
            system = self.detect_system()
            
            if system == "macos":
                print("💡 在macOS上安装sshpass:")
                print("   brew install sshpass")
                install = input("是否自动安装? (y/n): ").strip().lower()
                if install in ['y', 'yes']:
                    try:
                        subprocess.run(["brew", "install", "sshpass"], check=True, env=os.environ)
                        print("✅ sshpass安装成功")
                        return True
                    except subprocess.CalledProcessError:
                        print("❌ sshpass安装失败，请手动安装")
                        return False
            elif system == "ubuntu":
                print("💡 在Ubuntu上安装sshpass:")
                print("   sudo apt-get install sshpass")
                install = input("是否自动安装? (y/n): ").strip().lower()
                if install in ['y', 'yes']:
                    try:
                        subprocess.run(["sudo", "apt-get", "install", "-y", "sshpass"], check=True, env=os.environ)
                        print("✅ sshpass安装成功")
                        return True
                    except subprocess.CalledProcessError:
                        print("❌ sshpass安装失败，请手动安装")
                        return False
            else:
                print("💡 请手动安装sshpass")
                return False
        else:
            print("✅ sshpass已安装")
            return True
            
    def detect_system(self):
        """检测操作系统"""
        if sys.platform == "darwin":
            return "macos"
        elif os.path.exists("/etc/debian_version"):
            return "ubuntu"
        else:
            return "unknown"
            
    def setup_password_login(self):
        """设置密码登录"""
        print("\n🔐 密码登录设置")
        
        # 检测pexpect
        if not self.check_pexpect():
            return False

        # 从配置文件读取用户名
        current_user = self.config["source_server"]["user"]
        username = input(f"用户名 (当前: {current_user}): ").strip()
        if not username:
            username = current_user
            
        password = getpass.getpass("请输入服务器密码: ")
        if not password:
            print("❌ 密码不能为空")
            return False
            
        os.environ['SSHPASS'] = password
        self.config["login"]["method"] = "password"
        self.config["login"]["password"] = password
        self.config["source_server"]["user"] = username
        self.config_manager.save_config()
        
        print("✅ 密码登录配置已保存")
        return True
        
    def remote_backup(self):
        """远程备份流程"""
        print("\n🌐 远程备份")
        print("1. 密钥登录")
        print("2. 密码登录")
        print("3. 返回")
        
        while True:
            choice = input("\n请选择登录方式 (1-3): ").strip()
            
            if choice == "1":
                # 密钥登录
                cert_path = self.select_certificate()
                if cert_path:
                    self.config["login"]["method"] = "certificate"
                    self.config["login"]["certificate_path"] = cert_path
                    self.config_manager.save_config()
                    print("✅ 证书配置已保存")
                    
                    # 执行远程备份
                    self.execute_remote_backup()
                    break
                    
            elif choice == "2":
                # 密码登录
                if self.setup_password_login():
                    # 执行远程备份
                    self.execute_remote_backup()
                    break
                    
            elif choice == "3":
                print("返回主菜单")
                break
            else:
                print("❌ 无效选择，请重新输入")
                
    def execute_remote_backup(self):
        """执行远程备份"""
        print("\n🚀 开始远程备份...")
        
        try:
            backup_tool = ServerDockerBackupTool(self.config_manager)
            success = backup_tool.full_backup()
            
            if success:
                print("✅ 远程备份完成!")
                system_info = backup_tool.collect_source_system_info()
                backup_tool.create_backup_report(system_info)
                print(f"📁 备份文件位置: {backup_tool.backup_dir}")
            else:
                print("❌ 远程备份失败")
                
        except KeyboardInterrupt:
            print("\n备份被用户中断")
        except Exception as e:
            print(f"❌ 备份过程中发生错误: {e}")
            
    def find_local_backups(self):
        """查找本地可用的备份文件（支持当前目录和backup/目录）"""
        backup_files = glob.glob("*.gz") + glob.glob("backup/*.gz")
        backup_files = sorted(backup_files)
        return backup_files
        
    def local_backup(self):
        """本地备份流程"""
        print("\n💾 本地备份")
        backup_files = self.find_local_backups()
        if not backup_files:
            print("❌ 未找到可用的备份文件(.gz)")
            print("请先进行远程备份或确保备份文件存在")
            return
        print("📋 找到以下备份文件:")
        for i, backup_file in enumerate(backup_files, 1):
            file_size = os.path.getsize(backup_file) / (1024 * 1024)  # MB
            print(f"  {i}. {backup_file} ({file_size:.1f} MB)")
        while True:
            try:
                choice = input(f"\n请选择备份文件 (1-{len(backup_files)}): ").strip()
                choice_num = int(choice)
                if 1 <= choice_num <= len(backup_files):
                    selected_backup = backup_files[choice_num - 1]
                    print(f"✅ 已选择备份文件: {selected_backup}")
                    self.process_local_backup(selected_backup)
                    break
                else:
                    print("❌ 无效选择，请重新输入")
            except ValueError:
                print("❌ 请输入有效数字")
                
    def process_local_backup(self, backup_file):
        """处理本地备份文件"""
        print(f"\n🔧 处理本地备份文件: {backup_file}")
        
        # 创建处理目录
        timestamp = backup_file.replace('.gz', '').replace('.tar', '')
        process_dir = f"local_backup_{timestamp}"
        os.makedirs(process_dir, exist_ok=True)
        
        try:
            # 解压备份文件
            print("📦 解压备份文件...")
            subprocess.run(["tar", "-xzf", backup_file, "-C", process_dir], check=True, env=os.environ)
            print("✅ 备份文件解压完成")
            
            # 查找系统信息文件
            system_info_file = os.path.join(process_dir, "system_info.json")
            if os.path.exists(system_info_file):
                with open(system_info_file, 'r', encoding='utf-8') as f:
                    system_info = json.load(f)
                print("✅ 系统信息文件找到")
            else:
                print("⚠️  未找到系统信息文件，使用默认配置")
                system_info = {
                    "hostname": "local-backup",
                    "os_release": "Unknown",
                    "kernel": "Unknown",
                    "architecture": "x86_64",
                    "distribution": "unknown"
                }
            
            # 生成Dockerfile
            backup_tool = ServerDockerBackupTool(self.config_manager)
            backup_tool.backup_dir = process_dir
            dockerfile_content = backup_tool.generate_dockerfile(system_info)
            
            print("✅ 本地备份处理完成")
            print(f"📁 处理目录: {process_dir}")
            
        except subprocess.CalledProcessError as e:
            print(f"❌ 解压失败: {e}")
        except Exception as e:
            print(f"❌ 处理过程中发生错误: {e}")
            
    def cleanup_temp_files(self):
        """清除临时文件"""
        print("\n🧹 清除临时文件")
        
        temp_items = []
        # backup目录下的内容加入可清理项，但不包括backup目录本身
        if os.path.exists("backup") and os.path.isdir("backup"):
            for item in os.listdir("backup"):
                temp_items.append(os.path.join("backup", item))
        
        # 查找docker_backup_*目录（兼容旧版本）
        backup_dirs = glob.glob("docker_backup_*")
        temp_items.extend(backup_dirs)
        
        # 查找local_backup_*目录
        local_dirs = glob.glob("local_backup_*")
        temp_items.extend(local_dirs)
        
        # 查找临时gz文件
        temp_gz = glob.glob("*.gz")
        temp_items.extend(temp_gz)
        
        # 过滤掉主程序文件和 .gitkeep 文件
        protected_files = {"docker_backup_launcher.py", "docker_backup_cli.py", ".gitkeep"}
        temp_items = [item for item in temp_items if os.path.basename(item) not in protected_files and item != "backup/.gitkeep"]
        
        if not temp_items:
            print("✅ 没有找到需要清理的临时文件")
            return
        
        print("📋 找到以下临时文件/目录:")
        for i, item in enumerate(temp_items, 1):
            if os.path.isdir(item):
                print(f"  {i}. 📁 {item}/")
            else:
                print(f"  {i}. 📄 {item}")
        print(f"  {len(temp_items) + 1}. 全部删除")
        print(f"  {len(temp_items) + 2}. 取消")
        
        while True:
            try:
                choice = input(f"\n请选择要删除的项目 (1-{len(temp_items) + 2}): ").strip()
                choice_num = int(choice)
                
                if 1 <= choice_num <= len(temp_items):
                    # 删除单个项目
                    item = temp_items[choice_num - 1]
                    if os.path.isdir(item):
                        shutil.rmtree(item)
                        print(f"✅ 已删除目录: {item}")
                    else:
                        os.remove(item)
                        print(f"✅ 已删除文件: {item}")
                    break
                elif choice_num == len(temp_items) + 1:
                    # 全部删除
                    confirm = input("⚠️  确定要删除所有临时文件吗? (y/n): ").strip().lower()
                    if confirm in ['y', 'yes']:
                        for item in temp_items:
                            if os.path.isdir(item):
                                shutil.rmtree(item)
                            else:
                                os.remove(item)
                        print("✅ 所有临时文件已删除")
                    break
                elif choice_num == len(temp_items) + 2:
                    print("取消删除")
                    break
                else:
                    print("❌ 无效选择，请重新输入")
            except ValueError:
                print("❌ 请输入有效数字")
                
    def check_pexpect(self):
        """检查pexpect是否可用"""
        try:
            import pexpect
            print("✅ pexpect已安装")
            return True
        except ImportError:
            print("⚠️  未找到pexpect库，需要安装")
            install = input("是否自动安装pexpect? (y/n): ").strip().lower()
            if install in ['y', 'yes']:
                try:
                    print("📦 正在安装pexpect...")
                    subprocess.run([sys.executable, "-m", "pip", "install", "pexpect"], check=True, env=os.environ)
                    print("✅ pexpect安装成功")
                    return True
                except subprocess.CalledProcessError:
                    print("❌ pexpect安装失败，请手动安装: pip3 install pexpect")
                    return False
            else:
                print("❌ 需要pexpect库才能继续")
                return False
        
    def run(self):
        """运行启动器"""
        while True:
            self.show_menu()
            choice = input("\n请选择操作 (0-3): ").strip()
            
            if choice == "1":
                self.remote_backup()
            elif choice == "2":
                self.local_backup()
            elif choice == "3":
                self.cleanup_temp_files()
            elif choice == "0":
                print("👋 再见!")
                break
            else:
                print("❌ 无效选择，请重新输入")

def main():
    """主函数"""
    print("🐳 绿谷云服务器docker化迁移工具启动器")
    print("版本: 1.0.0")
    print("[配置说明] 所有配置文件已统一放在 config/ 目录下，如 config/config.json")
    try:
        launcher = BackupLauncher()
        launcher.run()
    except KeyboardInterrupt:
        print("\n\n程序被用户中断")
    except Exception as e:
        print(f"\n❌ 程序运行错误: {e}")

if __name__ == "__main__":
    main()
